var sjb_invest_selector = 'form#sjb'; var sjb_slider_selector = 'div.range'; var sjb_hidden_output = 'input.output-hidden'; // Slider Function jQuery(document).ready(function($) { (function(factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module. define(['jquery'], factory); } else { // Browser globals factory(jQuery); } } (function($) { /** * Range feature detection * @return {Boolean} */ function supportsRange() { var input = document.createElement('input'); input.setAttribute('type', 'range'); return input.type !== 'text'; } var pluginName = 'sjb', pluginInstances = [], inputrange = supportsRange(), defaults = { polyfill: true, rangeClass: 'sjb', disabledClass: 'sjb--disabled', fillClass: 'sjb__fill', handleClass: 'sjb__handle', startEvent: ['mousedown', 'touchstart', 'pointerdown'], moveEvent: ['mousemove', 'touchmove', 'pointermove'], endEvent: ['mouseup', 'touchend', 'pointerup'] }; function delay(fn, wait) { var args = Array.prototype.slice.call(arguments, 2); return setTimeout(function(){ return fn.apply(null, args); }, wait); } function debounce(fn, debounceDuration) { debounceDuration = debounceDuration || 100; return function() { if (!fn.debouncing) { var args = Array.prototype.slice.apply(arguments); fn.lastReturnVal = fn.apply(window, args); fn.debouncing = true; } clearTimeout(fn.debounceTimeout); fn.debounceTimeout = setTimeout(function(){ fn.debouncing = false; }, debounceDuration); return fn.lastReturnVal; }; } function Plugin(element, options) { this.$window = $(window); this.$document = $(document); this.$element = $(element); this.options = $.extend( {}, defaults, options ); this._defaults = defaults; this._name = pluginName; this.startEvent = this.options.startEvent.join('.' + pluginName + ' ') + '.' + pluginName; this.moveEvent = this.options.moveEvent.join('.' + pluginName + ' ') + '.' + pluginName; this.endEvent = this.options.endEvent.join('.' + pluginName + ' ') + '.' + pluginName; this.polyfill = this.options.polyfill; this.onInit = this.options.onInit; this.onSlide = this.options.onSlide; this.onSlideEnd = this.options.onSlideEnd; // Plugin should only be used as a polyfill if (this.polyfill) { // Input range support? if (inputrange) { return false; } } this.identifier = 'js-' + pluginName + '-' +(+new Date()); this.min = parseFloat(this.$element[0].getAttribute('min') || 0); this.max = parseFloat(this.$element[0].getAttribute('max') || 100); this.value = parseFloat(this.$element[0].value || this.min + (this.max-this.min)/2); this.step = parseFloat(this.$element[0].getAttribute('step') || 1); this.noChange = false; this.$fill = $('
'); this.$handle = $('
'); this.$range = $('
').insertAfter(this.$element).prepend(this.$fill, this.$handle); // visually hide the input this.$element.css({ 'position': 'absolute', 'width': '1px', 'height': '1px', 'overflow': 'hidden', 'opacity': '0' }); // Store context this.handleDown = $.proxy(this.handleDown, this); this.handleMove = $.proxy(this.handleMove, this); this.handleEnd = $.proxy(this.handleEnd, this); this.init(); // Attach Events var _this = this; this.$window.on('resize' + '.' + pluginName, debounce(function() { // Simulate resizeEnd event. delay(function() { _this.update(); }, 300); }, 20)); this.$document.on(this.startEvent, '#' + this.identifier + ':not(.' + this.options.disabledClass + ')', this.handleDown); // Listen to programmatic value changes this.$element.on('change' + '.' + pluginName, function(e, data) { if (data && data.origin === pluginName) { return; } var value = e.target.value, pos = _this.getPositionFromValue(value); _this.setPosition(pos); }); } Plugin.prototype.init = function() { if (this.onInit && typeof this.onInit === 'function') { this.onInit(); } this.update(); }; Plugin.prototype.lock = function(args) { this.lockvalue = args[0]; } Plugin.prototype.unlock = function() { this.lockvalue = -1; } Plugin.prototype.update = function() { var hideagain = 0; if (!this.$element.is(':visible')) { hideagain = 1; } if (hideagain) this.$element.closest('.hidethis').show(); this.handleWidth = this.$handle[0].offsetWidth; this.rangeWidth = this.$range[0].offsetWidth; this.maxHandleX = this.rangeWidth - this.handleWidth; this.grabX = this.handleWidth / 2; this.position = this.getPositionFromValue(this.value); if (hideagain) this.$element.closest('.hidethis').hide(); // Consider disabled state if (this.$element[0].disabled) { this.$range.addClass(this.options.disabledClass); } else { this.$range.removeClass(this.options.disabledClass); } this.setPosition(this.position); }; Plugin.prototype.handleDown = function(e) { e.preventDefault(); this.$document.on(this.moveEvent, this.handleMove); this.$document.on(this.endEvent, this.handleEnd); // If we click on the handle don't set the new position if ((' ' + e.target.className + ' ').replace(/[\n\t]/g, ' ').indexOf(this.options.handleClass) > -1) { return; } var posX = this.getRelativePosition(this.$range[0], e), handleX = this.getPositionFromNode(this.$handle[0]) - this.getPositionFromNode(this.$range[0]); this.setPosition(posX - this.grabX); if (posX >= handleX && posX < handleX + this.handleWidth) { this.grabX = posX - handleX; } }; Plugin.prototype.handleMove = function(e) { e.preventDefault(); var posX = this.getRelativePosition(this.$range[0], e); this.setPosition(posX - this.grabX); }; Plugin.prototype.handleEnd = function(e) { e.preventDefault(); this.$document.off(this.moveEvent, this.handleMove); this.$document.off(this.endEvent, this.handleEnd); var value, left, ppp; ppp = this.getPositionFromValue(this.min + this.step); value = this.getValueFromPosition(Math.round(this.position / ppp) * ppp); left = this.getPositionFromValue(value); // Update ui this.$fill[0].style.width = (left + this.grabX) + 'px'; this.$handle[0].style.left = left + 'px'; this.position = left; this.value = value; if (this.onSlideEnd && typeof this.onSlideEnd === 'function') { this.onSlideEnd(this.position, this.value); } }; Plugin.prototype.cap = function(pos, min, max) { if (pos < min) { return min; } if (pos > max) { return max; } return pos; }; Plugin.prototype.setPosition = function(pos) { var value, left, ppp; var tobe = ((this.getValueFromPosition(this.cap(pos, 0, this.maxHandleX)) / this.step) * this.step); if (tobe >= this.lockvalue && this.lockvalue > -1) { value = this.lockvalue; left = this.getPositionFromValue(this.lockvalue); } else { // Moving steps ppp = this.getPositionFromValue(this.min + this.step); value = this.getValueFromPosition(Math.round(pos / ppp) * ppp); left = this.cap(pos, 0, this.maxHandleX); } //Snapping steps // value = (this.getValueFromPosition(this.cap(pos, 0, this.maxHandleX)) / this.step) * this.step; // left = this.getPositionFromValue(value); // Update ui this.$fill[0].style.width = (left + this.grabX) + 'px'; this.$handle[0].style.left = left + 'px'; this.setValue(value); // Update globals this.position = left; this.value = value; if (this.onSlide && typeof this.onSlide === 'function') { this.onSlide(left, value); } }; Plugin.prototype.getPositionFromNode = function(node) { var i = 0; while (node !== null) { i += node.offsetLeft; node = node.offsetParent; } return i; }; Plugin.prototype.getRelativePosition = function(node, e) { return (e.pageX || e.originalEvent.clientX || e.originalEvent.touches[0].clientX || e.currentPoint.x) - this.getPositionFromNode(node); }; Plugin.prototype.getPositionFromValue = function(value) { var percentage, pos; percentage = (value - this.min)/(this.max - this.min); pos = percentage * this.maxHandleX; return pos; }; Plugin.prototype.getValueFromPosition = function(pos) { var percentage, value; percentage = ((pos) / (this.maxHandleX || 1)); value = this.step * Math.round((((percentage) * (this.max - this.min)) + this.min) / this.step); return Number((value).toFixed(2)); }; Plugin.prototype.setValue = function(value) { if (value !== this.value) { this.$element.val(value).trigger('change', {origin: pluginName}); } }; Plugin.prototype.destroy = function() { this.$document.off(this.startEvent, '#' + this.identifier, this.handleDown); this.$element .off('.' + pluginName) .removeAttr('style') .removeData('plugin_' + pluginName); // Remove the generated markup if (this.$range && this.$range.length) { this.$range[0].parentNode.removeChild(this.$range[0]); } // Remove global events if there isn't any instance anymore. pluginInstances.splice(pluginInstances.indexOf(this.$element[0]),1); if (!pluginInstances.length) { this.$window.off('.' + pluginName); } }; // A really lightweight plugin wrapper around the constructor, // preventing against multiple instantiations $.fn[pluginName] = function(options) { var args = [].slice.call(arguments); return this.each(function() { var $this = $(this), data = $this.data('plugin_' + pluginName); // Create a new instance. if (!data) { $this.data('plugin_' + pluginName, (data = new Plugin(this, options))); pluginInstances.push(this); } // Make it possible to access methods from public. // e.g `$element.sjb('method');` if (typeof options === 'string') { data[args.shift()](args); } }); }; })); var $ = jQuery; $('.openprivacy').on('click', function() { $('#privacymodal').show(); }); // Closes terms modal $('.closemodal').on('click', function() { $('#privacymodal').hide(); }); $("#completed").delay(3000).hide("slow"); /* Select all relevant invest slider forms */ $(sjb_invest_selector).each(function() { /* Initialize sliders */ var sliders = $(this).find('[data-sjb]'), x = $(this); sliders.change(sjbCalculate); var form = $(this), rates = sjb__rates["settings"], buttons = form.find('.sjb-control'); buttons.filter('.sjb-down').click(function() { var range = $(this).closest('.range').find('input[type=range]'); var v = parseFloat(range.val()); var s = parseFloat(range.attr('step')) || 1; var m = parseFloat(range.attr('min')); var n = v - s; if (n < m) range.val(m); else range.val(n); range.change(); }); buttons.filter('.sjb-up').click(function() { var range = $(this).closest('.range').find('input[type=range]'); var v = parseFloat(range.val()); var s = parseFloat(range.attr('step')) || 1; var m = parseFloat(range.attr('max')); var n = v + s; if (n > m) range.val(m); else range.val(n); range.change(); }); sliders.sjb({polyfill:false}); $(sliders[0]).change(); /* Show/hide application form */ $(".application").hide("slow"); $(".applybutton").click(function(event){ $(".application").slideToggle("slow"); event.preventDefault(); return false; }); }); }); // Calculates outputs function sjbCalculate(e) { /* Change relevent element's output value */ var $ = jQuery, form = $(this).closest(sjb_invest_selector), rates = sjb__rates["settings"], sliders = form.find(sjb_slider_selector), p = form.find(sjb_slider_selector).filter('.sjb-slider-principal'), principal = parseFloat(p.find('input[type=range]').val()) || 0, currency = rates.currency; /* Output principal */ p.find('output').text(rates.currency+principal.toString().sjb_separator(rates.separator)); outputs = sjb_calculate(10,principal,6,currency); outputs10 = sjb_calculate(10,principal,6,currency); outputs20 = sjb_calculate(20,principal,6,currency); outputs30 = sjb_calculate(30,principal,6,currency); var sjbinitial = outputs.sjbi * 100 / outputs.avei; var aveinitial = 100; var sjbannual = outputs.ap * 100; var aveannual = 100; var saving = outputs.is; var saving10 = outputs10.ts; var saving20 = outputs20.ts; var saving30 = outputs30.ts; // Build the bars form.find('#sjbgrowth').val(sjbinitial); form.find('#avegrowth').val(aveinitial); form.find('#sjbfees').val(sjbannual); form.find('#avefees').val(aveannual); /* Display the outputs */ //form.find('.invest').text(rates.currency+principal.toString().sjb_separator(rates.separator)); form.find('.sjbi').text(outputs.sjbi.toFixed(2)+'%'); form.find('.avei').text(outputs.avei.toFixed(2)+'%'); form.find('.sjba').text(outputs.ap.toFixed(2)+'%'); form.find('.avea').text(outputs.avea.toFixed(2)+'%'); form.find('.saving').text(rates.currency+saving.toString().sjb_separator(rates.separator)); form.find('.saving10').text(rates.currency+sjb_doubledigit(saving10,rates).sjb_rounding(rates)); form.find('.saving20').text(rates.currency+sjb_doubledigit(saving20,rates).sjb_rounding(rates)); form.find('.saving30').text(rates.currency+sjb_doubledigit(saving30,rates).sjb_rounding(rates)); } // Adds decimals function sjb_doubledigit(num,rates) { //if (rates.decimals == 'none') return Math.round(num).toString(); var n = num.toFixed(2); //if (rates.decimals == 'float') return n.replace('.00',''); return n; } // Calculates ouputs function sjb_calculate(term, principal, growth, currency) { var P = principal, T = term, G = growth, IF = 4, // SJB Initial Fee ICF = 0, // Comp Initial Fee A = 0, // SJB Incremental Annual Fee AF = 0, // SJB Total Annual Fee AP = 0, // Annual Percentage CA = 0, // Comp Incremental Annual Fee ACF = 0, // Comp Total Annual Fee TS = 0, // Total Saving TCF = 0; // Comp Total Fee if (currency == '$') { if (P > 1000000) {IF = 24000+ +(Math.max(0, (P - 1000000) * 1/100));} if (P <= 1000000) {IF = 21000+ +(Math.max(0, (P - 800000) * 1.5/100));} if (P <= 800000) {IF = 17000+ +(Math.max(0, (P - 600000) * 2/100));} if (P <= 600000) {IF = 12000+ +(Math.max(0, (P - 400000) * 2.5/100));} if (P <= 400000) {IF = P * 3/100;} } else { if (P > 1000000) {IF = 22750+ +(Math.max(0, (P - 1000000) * 1/100));} if (P <= 1000000) {IF = 19000+ +(Math.max(0, (P - 750000) * 1.5/100));} if (P <= 750000) {IF = 10000+ +(Math.max(0, (P - 300000) * 2/100));} if (P <= 300000) {IF = 4000+ +(Math.max(0, (P - 100000) * 3/100));} if (P <= 100000) {IF = P * 4/100;} } ICF = P * 5/100; var R = P; var S = P; if (P <= 300000) {AP = (P * 1/100);} if (P > 300000) {AP = 3000+ +((P - 300000) * 0.8/100);} if (P > 500000) {AP = 3000+ +1600+ +((P - 500000) * 0.6/100);} if (P > 750000) {AP = 3000+ +1600+ +1500+ +((P - 750000) * 0.4/100);} if (P > 1000000) {AP = 3000+ +1600+ +1500+ +1000+ +((P - 1000000) * 0.2/100);} AP = Number((AP*100/P).toFixed(2)); for (let i = 1; i <= T; i++) { // SJP Annual Fee R = R * (1+G/100); A = R * AP/100; R = R - A; AF = AF+ +A; // Comp Annual Fee S = S * (1 + G/100); CA = (S * 1/100); S = S - CA; ACF = ACF+ +CA; } // Total Saving TS = (ICF+ +ACF) - (IF+ +AF); // Percentages var IFP = IF*100/P; var AFP = AF*100/P; IS = ICF - IF; AS = ACF - AF; return {'sjbi':IFP,'avei':5,'sjba':AFP,'avea':1, 'is':IS,'as':AS,'ap':AP,'ts':TS}; } String.prototype.sjb_separator = function(sr) { if (sr == 'none') return this; else if (sr == 'apostrophe') var s = "'"; else if (sr == 'dot') var s = "."; else if (sr == 'comma') var s = ","; else var s = ' '; var str = this.split('.'); if (str[0].length >= 4) { str[0] = str[0].replace(/(\d)(?=(\d{3})+$)/g, '$1'+s); } if (sr == 'dot') var decimalsdevider = ','; else var decimalsdevider = '.'; return str.join(decimalsdevider); } String.prototype.sjb_rounding = function(rates) { var rr = rates.rounding; var r = 1; if (rr == 'tenround') var r = 10; if (rr == 'hundredround') var r = 100; if (rr == 'thousandround') var r = 1000; if (rr == 'noround') var num = this; else var num = Math.round(this / r) * r; var rs = rates.separator; if (rs == 'none') return num; if (rs == 'apostrophe') var s = "'"; else if (rs == 'dot') var s = "."; else if (rs == 'comma') var s = ","; else var s = ' '; var str = num.toString().split('.'); if (str[0].length >= 4) { str[0] = str[0].replace(/(\d)(?=(\d{3})+$)/g, '$1'+s); } if (rs == 'dot') var decimalsdevider = ','; else var decimalsdevider = '.'; return str.join(decimalsdevider); }